#ifndef PRIMITIVE_H
#define PRIMITIVE_H

#define PI       3.14159265358979323846
#include "Node.h"
#include <math.h>
#pragma once


class Primitive
{
protected:
	float distance(float x1, float y1, float z1, float x2, float y2, float z2)
	{
		return sqrt(pow(x1-x2,2) +pow(y1-y2,2) + pow(z1-z2,2));
	}

public:

	string id;
	material* mat;
	texture* tex;
	texture* t;
	virtual void render() = 0;
	void draw(material* fatherMat,texture* fatherText)
	{
		t=tex;
		if(t->textureID == "inherit")
				t=fatherText;

		if(t->textureID == "none")
			glDisable(GL_TEXTURE_2D);
		else
		{
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D,t->pixmapID);
		}

		material* m=mat;
		if(m->materialID == "inherit")
			m = fatherMat;
		applyMaterial(m);
		render();
		glDisable(GL_TEXTURE_2D);
	}

	void applyMaterial(material* m)
	{

		//glEnable(GL_COLOR_MATERIAL);
		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,m->emission);
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, m->ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, m->diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, m->specular);
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m->shininess * 128.0);

	}
};

class Rectangle : public Primitive {
public:
	Rectangle() {};
	Rectangle(float _x1,float _y1,float _x2,float _y2):
		x1(_x1),y1(_y1),x2(_x2),y2(_y2) {}
	float x1;
	float x2;
	float y1;
	float y2;
	void render(){
		float lenghtX = abs(x1-x2);
		float lenghtY = abs(y1-y2);
		glBegin(GL_QUADS);
			glNormal3f(0,0,1);
			glTexCoord2d( 0.0 , 0.0 );  glVertex2f(x1, y1);
			glTexCoord2d( lenghtX/t->length_s , 0.0); glVertex2f(x2, y1); 
			glTexCoord2d( lenghtX/t->length_s , lenghtY/t->length_t); glVertex2f(x2, y2); 
			glTexCoord2d(0.0,lenghtY/t->length_t); glVertex2f(x1, y2); 
		glEnd();
	}
};

class Triangle : public Primitive {
public:
	Triangle() {}
	Triangle(float _x1,float _y1,float _z1,float _x2,float _y2,float _z2,float _x3,float _y3,float _z3):
		x1(_x1),y1(_y1),z1(_z1),x2(_x2),y2(_y2),z2(_z2),x3(_x3),y3(_y3),z3(_z3) {}
	float x1;
	float y1;
	float z1;
	float x2;
	float y2;
	float z2;
	float x3;
	float y3;
	float z3;
	void render()
	{
		
		float AB[3] = {(x2-x1)/distance(x1,y1,z1,x2,y2,z2),(y2-y1)/distance(x1,y1,z1,x2,y2,z2),(z2-z1)/distance(x1,y1,z1,x2,y2,z2)};
		float AC[3] = {(x3-x1)/distance(x1,y1,z1,x3,y3,z3),(y3-y1)/distance(x1,y1,z1,x3,y3,z3),(z3-z1)/distance(x1,y1,z1,x3,y3,z3)};
		float cosAlfa =abs(AB[0]*AC[0]+AB[1]*AC[1]+AB[2]*AC[2]);
		
		float AD = distance(x1,y1,z1,x2,y2,z2) * cosAlfa;
		float AE = distance(x1,y1,z1,x2,y2,z2)*sin(acos(cosAlfa));
		float normal[] = {(x2-x1)*(z3-z1) - (z2-z1)*(y3-y1) , (z2-z1)*(x3-x1) - (x2-x1)*(z3-z1), (x2-x1)*(y3-y1) - (y2-y1)*(x3-x1)};
		glBegin(GL_TRIANGLES);
			glNormal3f(normal[0],normal[1],normal[2]);
			glTexCoord2d( distance(x1,y1,z1,x2,y2,z2)/t->length_s , 0.0 ); glVertex3d(x1,y1,z1);
			glTexCoord2d( AD/t->length_s , AE/t->length_t ); glVertex3d(x2,y2,z2); 
			glTexCoord2d( 0.0, 0.0); glVertex3d(x3,y3,z3);  
		glEnd();
	}
};

class Cylinder : public Primitive
{
public:
	Cylinder() {}
	Cylinder(float _base, float _top, float _height, int _slices, int _stacks):
	  base(_base),top(_top),height(_height),slices(_slices),stacks(_stacks) {}
	float base;
	float top;
	float height;
	int slices;
	int stacks;
	void render()
	{
		GLUquadric* glQ = gluNewQuadric();
		gluQuadricOrientation(glQ, GLU_OUTSIDE);
		gluQuadricTexture(glQ,GL_TRUE);
		gluCylinder(glQ,base,top,height,slices,stacks);
		//base disk
		glPushMatrix();
		glRotated(180,0,1,0);
		gluDisk(glQ,0,base,slices,stacks);
		glPopMatrix();
		//top disk
		glPushMatrix();
		glTranslated(0,0,height);
		gluDisk(glQ,0,top,slices,stacks);
		glPopMatrix();

	}
};

class Sphere : public Primitive
{
public:
	Sphere() {}
	Sphere(float _radius, int _slices, int _stacks):
	  radius(_radius),slices(_slices),stacks(_stacks) {}
	float radius;
	int slices;
	int stacks;
	void render()
	{
		GLUquadric* glQ = gluNewQuadric();
		gluQuadricOrientation(glQ, GLU_OUTSIDE);
		gluQuadricTexture(glQ,GL_TRUE);
		gluSphere(glQ,radius,slices,stacks);
	}
};

class Torus : public Primitive
{
public:
	Torus() {}
	Torus(float _inner, float _outer, int _slices, int _loops):
		inner(_inner),outer(_outer),slices(_slices),loops(_loops) {}
	float inner;
	float outer;
	int slices;
	int loops;
	void render()
	{
		GLUquadric* glQ = gluNewQuadric();
		gluQuadricOrientation(glQ, GLU_OUTSIDE);
		gluQuadricTexture(glQ,GL_TRUE);
		glutSolidTorus(inner,outer,slices,loops);
	}
};

class Hexagon : public Primitive
{
public:
	Hexagon() {}
	Hexagon(float _radius,float _height):
	radius(_radius),height(_height) 
	{
		for(int i = 6; i > 0; --i) {
			float x = radius * sin(i/6.0*2*PI);
			float y = radius * cos(i/6.0*2*PI);
			vertices.push_back(make_pair(x,y));
		}
	}
	float radius;
	float height;
	vector< pair<float,float> > vertices;
	void render()
	{
		//top face
		glBegin(GL_POLYGON);
		glNormal3f(0,0,1);
		for(size_t i = 0; i < vertices.size(); ++i)
		{
			glTexCoord2d( vertices[i].second + 0.5, vertices[i].first + 0.5); glVertex2d(vertices[i].first,vertices[i].second);
		}
		glEnd();

		//bottom face
		glBegin(GL_POLYGON);
		glNormal3f(0,0,-1);
		for(size_t i=vertices.size()-1; i != 0; --i)
		{
			glTexCoord2d( vertices[i].second + 0.5, vertices[i].first + 0.5); glVertex3d(vertices[i].first,vertices[i].second,-height);
		}
		glEnd();


		//lados
		for(size_t i = 0; i < vertices.size(); ++i)
		{
			size_t i2 = i+1;
			if(i2==vertices.size())
				i2=0;
			glBegin(GL_POLYGON);
			
			glNormal3f(vertices[i2].first-vertices[i2].first,vertices[i2].second-vertices[i].second,0);

			glVertex3d(vertices[i].first,vertices[i].second,0);
			glVertex3d(vertices[i].first,vertices[i].second,-height);
			glVertex3d(vertices[i2].first,vertices[i2].second,-height);
			glVertex3d(vertices[i2].first,vertices[i2].second,0);
			glEnd();
		}
		
	}
};

#endif